﻿// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.


using AutoMapper;
using Wlmy.Ids4.Common;
using IdentityServer4.EntityFramework.DbContexts;
using IdentityServer4.EntityFramework.Mappers;
using IdentityServer4.Services;
using IdentityServerApi.Filter;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using Wlmy.Ids4.Model;
using Microsoft.AspNetCore.Http;
using Serilog;
using Autofac;
using Wlmy.Ids4.Filter;
using IdentityModel;

namespace Wlmy.Ids4
{
    public class Startup
    {

        public IConfiguration Configuration { get; }
        public IWebHostEnvironment Environment { get; }

        public Startup(IConfiguration configuration, IWebHostEnvironment environment)
        {
            Configuration = configuration;
            Environment = environment;
        }

        public void ConfigureServices(IServiceCollection services)
        {
            // uncomment, if you want to add an MVC-based UI
            services
                .AddControllers(x =>
                {
                    x.Filters.Add(typeof(ResultFilter));
                    x.Filters.Add(typeof(ExceptionsFilter));
                })
                .AddNewtonsoftJson(options =>
                {
                    //忽略循环引用
                    options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
                    //设置时间格式
                    options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
                    options.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
                });
            services.AddSameSiteCookiePolicy();
            services.AddCors(x => x.AddPolicy("cors", policy => policy.WithOrigins("http://localhost:8080").AllowAnyMethod().AllowAnyHeader().AllowCredentials()));
            
            string connectionString = Configuration.GetConnectionString("SqlLite");
            var migrationsAssembly = "Wlmy.Ids4.Model";
            ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder =>
            {
                builder
                    .AddFilter((category, level) =>
                        category == DbLoggerCategory.Database.Command.Name
                        && level == LogLevel.Information)
                    .AddConsole();
            });
            services.AddDbContext<CustomDbContext>(options => options.UseSqlite(connectionString).UseLoggerFactory(MyLoggerFactory));
            services.Configure<IdentityOptions>(options =>
            {
                // Default Password settings.
                options.Password.RequireDigit = true;
                options.Password.RequireLowercase = false;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireUppercase = false;
                options.Password.RequiredLength = 6;
                options.Password.RequiredUniqueChars = 0;
                options.ClaimsIdentity.UserNameClaimType = JwtClaimTypes.Name;
                options.ClaimsIdentity.RoleClaimType = JwtClaimTypes.Role;
                options.ClaimsIdentity.UserIdClaimType = JwtClaimTypes.Subject;
            });
            services.AddIdentity<User, Role>()
                .AddRoles<Role>()
                .AddEntityFrameworkStores<CustomDbContext>()
                .AddDefaultTokenProviders();
            var builder = services.AddIdentityServer(options =>
            {
                options.Events.RaiseSuccessEvents = true;
                options.Events.RaiseFailureEvents = true;
                options.Events.RaiseErrorEvents = true;
                options.Events.RaiseInformationEvents = true;
                options.MutualTls.Enabled = true;
                options.MutualTls.DomainName = "mtls";
                options.Cors.CorsPaths.Add(new PathString("/connect/authorize"));
                options.Cors.CorsPaths.Add(new PathString("/connect/authorize/callback"));
                options.Cors.CorsPaths.Add(new PathString("/api/account/login"));
                options.Cors.CorsPaths.Add(new PathString("/api/account/logout"));
                options.UserInteraction.LoginUrl = "http://localhost:8080/#/login";
                options.UserInteraction.LogoutUrl = "http://localhost:8080/#/logout";
            })
                .AddAspNetIdentity<User>()
                // this adds the config data from DB (clients, resources)
                .AddConfigurationStore(options =>
                {
                    options.ConfigureDbContext = b =>
                        b.UseSqlite(connectionString,
                            sql => sql.MigrationsAssembly(migrationsAssembly));
                })
                // this adds the operational data from DB (codes, tokens, consents)
                .AddOperationalStore(options =>
                {
                    options.ConfigureDbContext = b =>
                        b.UseSqlite(connectionString,
                            sql => sql.MigrationsAssembly(migrationsAssembly));
                    // this enables automatic token cleanup. this is optional.
                    options.EnableTokenCleanup = true;
                    // options.TokenCleanupInterval = 15; // frequency in seconds to cleanup stale grants. 15 is useful during debugging
                });
            // not recommended for production - you need to store your key material somewhere secure
            builder.AddDeveloperSigningCredential();
            services.AddAuthentication();
            services.AddSwaggerGen(x => x.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo { Title = "IdentityServerApi", Version = "v1" }));
            services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
            services.AddHttpContextAccessor();
        }

        public void Configure(IApplicationBuilder app)
        {
            InitializeDatabase(app);
            app.UseCors("cors");
            app.UseCookiePolicy();
            app.UseSerilogRequestLogging();
            if (Environment.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            StaticInfo.ServiceProvider = app.ApplicationServices;
            app.UseSwagger();
            
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "IdentityServerApi");
                // 将swagger首页，设置成我们自定义的页面，记得这个字符串的写法：解决方案名.index.html
                c.RoutePrefix = "swagger"; //路径配置，设置为空，表示直接在根域名（localhost:8001）访问该文件,注意localhost:8001/swagger是访问不到的，去launchSettings.json把launchUrl去掉，如果你想换一个路径，直接写名字即可，比如直接写c.RoutePrefix = "doc";
            });
            //app.UseHttpsRedirection();
            // uncomment if you want to add MVC
            app.UseStaticFiles();
            
            app.UseRouting();
            app.Use(async (context, next) =>
            {
                var a = context.Response.Cookies;
                // Call the next delegate/middleware in the pipeline
                await next();
            });
            app.UseIdentityServer();
            // uncomment, if you want to add MVC
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapDefaultControllerRoute();
            });
        }
        private void InitializeDatabase(IApplicationBuilder app)
        {
            using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
            {
                serviceScope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();
                
                var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
                var identityContext = serviceScope.ServiceProvider.GetRequiredService<CustomDbContext>();
                context.Database.Migrate();
                identityContext.Database.Migrate();
                if (!identityContext.Users.Any())
                {
                    var userManager = serviceScope.ServiceProvider.GetRequiredService<UserManager<User>>();
                    identityContext.Roles.Add(Config.Role);
                    userManager.CreateAsync(Config.User);
                    identityContext.SaveChanges();
                }
                if (!context.Clients.Any())
                {
                    foreach (var client in Config.Clients)
                    {
                        context.Clients.Add(client.ToEntity());
                    }
                    context.SaveChanges();
                }

                if (!context.IdentityResources.Any())
                {
                    foreach (var resource in Config.IdentityResources)
                    {
                        context.IdentityResources.Add(resource.ToEntity());
                    }
                    context.SaveChanges();
                }

                if (!context.ApiResources.Any())
                {
                    foreach (var resource in Config.ApiResource)
                    {
                        context.ApiResources.Add(resource.ToEntity());
                    }
                    foreach (var scope in Config.ApiScopes)
                    {
                        context.ApiScopes.Add(scope.ToEntity());
                    }
                    context.SaveChanges();
                }
            }
        }
        public void ConfigureContainer(ContainerBuilder containerBuilder)
        {
            containerBuilder.RegisterModule<AutofacModule>();
        }
    }
}
